fix(gate): never leave the Gittensory Gate check stuck in_progress - #655
Conversation
Production incident: the 'Gittensory Gate is evaluating' check on PR #650 stayed in_progress for ~8h. Root cause: maybePublishPrPublicSurface posts the pending (in_progress) gate check, then does D1 + GitHub + Gittensor work before the completing PATCH — with no request timeouts and no failure finalization. If anything in that gap hangs (a bare fetch to a slow upstream) or throws, the completing PATCH never runs and the check is orphaned forever; the caller's .catch only logs it. Confirmed via prod: miner detection was a cache hit at 13:35:08, the pending check posted at 13:35:11, then nothing — no completion, no error row (a hang, not a caught throw), while prod D1 was intermittently overloaded. Fix: - Bound every external call in the gate window with a request timeout so a hang becomes a catchable error: AbortSignal.timeout on the GitHub App fetches + the Octokit instance (src/github/app.ts) and on the Gittensor API client (src/gittensor/api.ts, the single fetchJson chokepoint). - Wrap the pending-post -> completion window in try/catch. On any failure, finalize the SAME check run to a neutral, non-blocking 'could not finish evaluating — will re-run' state (createOrUpdateErroredGateCheckRun) and audit it, so the Gate is always terminal and never hangs. Only finalizes when a real conclusion was not already published (no clobbering verdicts). Test proves a failed completion PATCH is followed by a neutral finalize of the same check id. Coverage holds above the 97% gate.
|
Note Gittensory Gate skippedPR closed before full evaluation. No late first comment was created.
💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →. Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers. |
|
gittensory · advisory review Reviewed 5 changed file(s) — two independent AI reviewers. Suggested action: ✅ Safe to merge — both reviewers found no blocking issues. Reviewer A · Suggestions
Worth double-checking
Reviewer B · Suggestions
Worth double-checking
|
|
Superagent didn't find any vulnerabilities or security issues in this PR. |
… them silently The gittensory-jobs consumer had no dead_letter_queue, so a webhook job that fails its retries was silently dropped (no record once webhook_events isn't reached). Route exhausted jobs to a new gittensory-jobs-dlq landing queue (created on the account; no consumer, matching the house pattern) and set max_retries explicitly. Validated with wrangler deploy --dry-run. Co-requisite infra (already provisioned): `wrangler queues create gittensory-jobs-dlq`.
PR #655 finalizes the pending in_progress Gate check on any failure that throws. But a 403 on the completion call is classified as permission_missing and returns without throwing, so the catch never runs and the pending check is orphaned in_progress forever. The pending check already posted (pendingGateCheckRunId is set), proving the App had Checks:write, so a 403 at completion is almost always a transient secondary-rate-limit rather than a real revocation. Finalize the pending check to neutral in the permission_missing branch too (mirroring the catch); a genuine revocation makes this PATCH 403 as well and it is swallowed. Closes #730 Co-authored-by: ghost <49853598+JSONbored@users.noreply.github.com>
Production incident: Gittensory Gate stuck
in_progressfor ~8h (PR #650)Root cause
maybePublishPrPublicSurfaceposts the "Gittensory Gate is evaluating" check (status: in_progress) early, then does a block of D1 + GitHub + Gittensor work before the completing PATCH — with no request timeouts and no failure finalization. If anything in that window hangs (a barefetchto a slow/half-open upstream) or throws, the completing PATCH never runs and the check is orphanedin_progressforever; the caller's.catchonly logs it.Confirmed from prod (
audit_events/webhook_events): miner detectioncache_hit13:35:08 → pending posted 13:35:11 → then nothing (no completion, no error row → a hang, not a caught throw), while prod D1 was intermittently overloaded. Since miner detection was cached, the hang was in the completion's own un-timed GitHub calls.Fix (3 commits)
AbortSignal.timeouton the GitHub App fetches + the Octokit instance (src/github/app.ts, 12s) and the Gittensor API client (src/gittensor/api.ts, 10s).try/catch; on any failure, finalize the same check run to a neutral, non-blocking"Gittensory Gate — could not finish evaluating — will re-run"state (createOrUpdateErroredGateCheckRun) + agithub_app.gate_finalized_on_erroraudit. Guarded so it never clobbers a realsuccess/failureverdict.gittensory-jobsconsumer had nodead_letter_queue, so a webhook job that exhausts its retries was silently dropped. Route exhausted jobs to a newgittensory-jobs-dlqlanding queue (already provisioned on the account; no consumer, matching the house pattern) and setmax_retriesexplicitly. Validated withwrangler deploy --dry-run.Net effect: a transient blip leaves a completed (harmless) check that re-runs on the next push, and a persistently-failing job lands in the DLQ for inspection instead of vanishing.
Test
A queue test drives a confirmed
in_progresspost, fails the completion PATCH (500), and asserts the same check id is then PATCHed tostatus: completed, conclusion: neutral, title: "Gittensory Gate — could not finish evaluating", with the audit row recorded. Full suite green; coverage holds above the 97% gate; Workers-runtime tests pass.Immediate remediation for PR #650
Re-trigger the gate (push any commit, or tick "Re-run Gittensory review" in the panel) — the orphaned run is owned by
gittensory[bot], so only a fresh run from the app supersedes it.Related
Separate from #652 (AI review + BYOK) and #656 (timestamp-default fix); all came out of the same gate-hang investigation.